﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Dynamic;
using System.IO;
using System.Xml.Serialization;
using System.Web;
using System.Reflection;
using System.Runtime.Serialization.Formatters.Binary;
using System.Runtime.Remoting.Messaging;
using System.Threading.Tasks;

namespace System
{
    public static class ObjectConvertExtension
    {
        /// <summary>
        /// tries a to convert a object to a byte
        /// </summary>
        /// <param name="anonymousObject">Current object. Can not be null.</param>
        /// <returns>byte object of the object.</returns>
        public static byte ToByte(this object anonymousObject)
        {
            if (anonymousObject == null) return 0;

            try
            {
                return Convert.ToByte(anonymousObject);
            }
            catch
            {
                return 0;
            }
        }

        /// <summary>
        /// tries a to convert a object to a Char
        /// </summary>
        /// <param name="anonymousObject">Current object. Can not be null.</param>
        /// <returns>Char object of the object.</returns>
        public static char ToChar(this object anonymousObject)
        {
            if (anonymousObject == null) return Char.MinValue;

            try
            {
                return Convert.ToChar(anonymousObject);
            }
            catch
            {
                return Char.MinValue;
            }
        }

        /// <summary>
        /// tries a to convert a object to a Decimal
        /// </summary>
        /// <param name="anonymousObject">Current object. Can not be null.</param>
        /// <returns>Decimal object of the object.</returns>
        public static Decimal ToDecimal(this object anonymousObject)
        {
            if (anonymousObject == null) return 0;

            try
            {
                return Convert.ToDecimal(anonymousObject);
            }
            catch
            {
                return 0;
            }
        }

        /// <summary>
        /// tries a to convert a object to a Double
        /// </summary>
        /// <param name="anonymousObject">Current object. Can not be null.</param>
        /// <returns>Double object of the object.</returns>
        public static Double ToDouble(this object anonymousObject)
        {
            if (anonymousObject == null) return 0;

            try
            {
                return Convert.ToDouble(anonymousObject);
            }
            catch
            {
                return 0;
            }
        }

        /// <summary>
        /// tries a to convert a object to a Short
        /// </summary>
        /// <param name="anonymousObject">Current object. Can not be null.</param>
        /// <returns>Short object of the object.</returns>
        public static short ToShort(this object anonymousObject)
        {
            if (anonymousObject == null) return 0;

            try
            {
                return Convert.ToInt16(anonymousObject);
            }
            catch
            {
                return 0;
            }
        }

        /// <summary>
        /// tries a to convert a object to a Int16
        /// </summary>
        /// <param name="anonymousObject">Current object. Can not be null.</param>
        /// <returns>Int16 object of the object.</returns>
        public static Int16 ToInt16(this object anonymousObject)
        {
            if (anonymousObject == null) return 0;

            try
            {
                return Convert.ToInt16(anonymousObject);
            }
            catch
            {
                return 0;
            }
        }

        /// <summary>
        /// tries a to convert a object to a Int32
        /// </summary>
        /// <param name="anonymousObject">Current object. Can not be null.</param>
        /// <returns>Int32 object of the object.</returns>
        public static Int32 ToInt32(this object anonymousObject)
        {
            if (anonymousObject == null) return 0;

            try
            {
                return Convert.ToInt32(anonymousObject);
            }
            catch
            {
                return 0;
            }
        }

        /// <summary>
        /// tries a to convert a object to a Int
        /// </summary>
        /// <param name="anonymousObject">Current object. Can not be null.</param>
        /// <returns>Int object of the object.</returns>
        public static int ToInt(this object anonymousObject)
        {
            if (anonymousObject == null) return 0;

            try
            {
                return Convert.ToInt32(anonymousObject);
            }
            catch
            {
                return 0;
            }
        }

        /// <summary>
        /// tries a to convert a object to a Int64
        /// </summary>
        /// <param name="anonymousObject">Current object. Can not be null.</param>
        /// <returns>Int64 object of the object.</returns>
        public static Int64 ToInt64(this object anonymousObject)
        {
            if (anonymousObject == null) return 0;

            try
            {
                return Convert.ToInt64(anonymousObject);
            }
            catch
            {
                return 0;
            }
        }

        /// <summary>
        /// tries a to convert a object to a long
        /// </summary>
        /// <param name="anonymousObject">Current object. Can not be null.</param>
        /// <returns>long object of the object.</returns>
        public static long ToLong(this object anonymousObject)
        {
            if (anonymousObject == null) return 0;

            try
            {
                return Convert.ToInt64(anonymousObject);
            }
            catch
            {
                return 0;
            }
        }

        /// <summary>
        /// tries a to convert a object to a bool
        /// </summary>
        /// <param name="anonymousObject">Current object. Can not be null.</param>
        /// <returns>bool object of the object.</returns>
        public static bool ToBool(this object anonymousObject)
        {
            if (anonymousObject == null) return false;
            if (anonymousObject.GetType() == typeof(int))
            {
                if (anonymousObject.ToInt() == 1) return true;

                return false;
            }

            try
            {
                return Convert.ToBoolean(anonymousObject);
            }
            catch
            {
                return false;
            }
        }

        /// <summary>
        /// tries a to convert a object to a Boolean
        /// </summary>
        /// <param name="anonymousObject">Current object. Can not be null.</param>
        /// <returns>Boolean object of the object.</returns>
        public static Boolean ToBoolean(this object anonymousObject)
        {
            if (anonymousObject == null) return false;
            if (anonymousObject.GetType() == typeof(int))
            {
                if (anonymousObject.ToInt() == 1) return true;

                return false;
            }

            try
            {
                return Convert.ToBoolean(anonymousObject);
            }
            catch
            {
                return false;
            }
        }

        /// <summary>
        /// Returns String.Empty on null objects or null instances
        /// </summary>
        /// <param name="anonymousObject">Current object. Can not be null.</param>
        /// <returns>ToString() or String.Empty if NULL</returns>
        public static string ToStringEx(this object anonymousObject)
        {
            if (anonymousObject == null) return String.Empty;

            try
            {
                return anonymousObject.ToString();
            }
            catch
            {
                return String.Empty;
            }
        }

        /// <summary>
        /// Creates a new instance of the type you want to cast or convert too. Object must match casting type passed.
        /// </summary>
        /// <typeparam name="T">The type of the members of values.</typeparam>
        /// <param name="anonymousObject">Current object. Can not be null.</param>
        /// <returns>new instance of type even if object is null</returns>
        public static T ToNewType<T>(this object anonymousObject)
        {
            try
            {
                T typeobj = Activator.CreateInstance<T>();

                if (anonymousObject == null) return typeobj;
                if (anonymousObject.GetType() != typeobj.GetType()) return typeobj;

                typeobj = (T)anonymousObject;

                return typeobj;
            }
            catch (Exception ex)
            {
                throw ex;
            }
        }

        /// <summary>
        /// The type you want to cast or convert too. Object must match casting type passed.
        /// </summary>
        /// <typeparam name="T">The type of the members of values.</typeparam>
        /// <param name="anonymousObject">Current object. Can not be null.</param>
        /// <returns>new instance of type</returns>
        public static T ToType<T>(this object anonymousObject)
        {
            try
            {
                if (anonymousObject == null) return anonymousObject.ToNewType<T>();

                return (T)anonymousObject;
            }
            catch (Exception ex)
            {
                throw ex;
            }
        }

        /// <summary>
        /// Checks an object for null
        /// </summary>
        /// <param name="anonymousObject">Current object. Can be null.</param>
        /// <returns>True if is null and False if is not null.</returns>
        public static bool isNull(this object anonymousObject)
        {
            return (anonymousObject == null);
        }

        public static bool IsInstanceOfType(this object anonymousObject, object ObjectToCompare)
        {
            try
            {
                if (anonymousObject == null) return false;
                if (ObjectToCompare == null) return false;

                object typeobj = Activator.CreateInstance(ObjectToCompare.GetType());

                return anonymousObject.GetType().IsInstanceOfType(typeobj);
            }
            catch
            {
                return false;
            }
        }

        public static bool IsInstanceOfType<T>(this object anonymousObject)
        {
            try
            {
                if (anonymousObject == null) return false;

                T typeobj = Activator.CreateInstance<T>();

                return anonymousObject.GetType().IsInstanceOfType(typeobj);
            }
            catch
            {
                return false;
            }
        }

        public static bool IsInstanceOfType(this object anonymousObject, string FullName)
        {
            try
            {
                if (anonymousObject == null) return false;

                Type tType = Type.GetType(FullName);
                object typeobj = Activator.CreateInstance(tType);

                return anonymousObject.GetType().IsInstanceOfType(typeobj);
            }
            catch
            {
                return false;
            }
        }

        /// <summary>
        /// Automatically binds the values of a property to another class. Note: property name case is ignored.
        /// Creates a new instance of the class that is returned.
        /// </summary>
        /// <typeparam name="T">The type of the members of values.</typeparam>
        /// <param name="anonymousObject">Current object. Can not be null.</param>
        /// <returns>the class you want to bind the property values to.</returns>
        public static T BindPropertiesValues<T>(this object anonymousObject)
        {
            try
            {
                if (anonymousObject == null) return (T)anonymousObject;

                Type tSrcObject = anonymousObject.GetType();
                T typeobj = Activator.CreateInstance<T>();
                Type tDesObject = typeobj.GetType();


                PropertyInfo[] pfSrcobjs = tSrcObject.GetProperties();
                PropertyInfo[] pfDescobjs = tDesObject.GetProperties();

                Parallel.ForEach(pfDescobjs, pitem =>
                    {
                        var pinfo = pfSrcobjs.Where(
                                                p =>
                                                    p.Name.Equals(pitem.Name, StringComparison.OrdinalIgnoreCase) && // Same Property name
                                                    p.CanWrite && // make sure we can write to the property
                                                    p.MemberType == MemberTypes.Property && // Make sure is a property
                                                    p.PropertyType == pitem.PropertyType // make sure the same data type
                                                ).FirstOrDefault();

                        if (pinfo != null)
                        {
                            try { pitem.SetValue(typeobj, Convert.ChangeType(pinfo.GetValue(anonymousObject, null), pinfo.PropertyType), null); }
                            catch { }
                        }
                    }
                );

                return typeobj;
            }
            catch (Exception ex)
            {
                throw ex;
            }
        }

        public static T BindPropertiesValues<T>(this object anonymousObject, IDictionary<string, string> Mapping)
        {
            try
            {
                if (anonymousObject == null) throw new NullReferenceException();

                Type tSrcObject = anonymousObject.GetType();
                T typeobj = Activator.CreateInstance<T>();
                Type tDesObject = typeobj.GetType();

                PropertyInfo[] pfSrcobjs = tSrcObject.GetProperties();
                PropertyInfo[] pfDescobjs = tDesObject.GetProperties();

                Parallel.ForEach(pfSrcobjs, pitem =>
                {
                    var pMapping = Mapping.Where(p => p.Key.Equals(pitem.Name, StringComparison.OrdinalIgnoreCase)).FirstOrDefault();

                    if (pMapping.isNull()) return;

                    var pinfo = pfDescobjs.Where(
                                                p =>
                                                    p.Name.Equals(pMapping.Value, StringComparison.OrdinalIgnoreCase) && // Same Property name
                                                    p.CanWrite && // make sure we can write to the property
                                                    p.MemberType == MemberTypes.Property // Make sure is a property
                                                ).FirstOrDefault();

                    if (pinfo.isNull() == false)
                    {
                        try { pinfo.SetValue(typeobj, Convert.ChangeType(pitem.GetValue(anonymousObject, null), pinfo.PropertyType), null); }
                        catch { }
                    }
                }
                );

                return typeobj;
            }
            catch (Exception ex)
            {
                throw ex;
            }
        }

        public static T BindPropsToVariblesValues<T>(this object anonymousObject)
        {
            try
            {
                if (anonymousObject == null) return (T)anonymousObject;

                Type tSrcObject = anonymousObject.GetType();
                T typeobj = Activator.CreateInstance<T>();
                Type tDesObject = typeobj.GetType();


                PropertyInfo[] pfSrcobjs = tSrcObject.GetProperties();
                FieldInfo[] pfDescobjs = tDesObject.GetFields();

                Parallel.ForEach(pfDescobjs, pitem =>
                    {
                        var pinfo = pfSrcobjs.Where(
                                                p =>
                                                    p.Name.Equals(pitem.Name, StringComparison.OrdinalIgnoreCase) && // Same Property name
                                                    p.CanWrite // make sure we can write to the property
                            //p.PropertyType == pitem.FieldType
                                                ).FirstOrDefault();

                        if (pinfo != null)
                        {
                            try { pitem.SetValue(typeobj, Convert.ChangeType(pinfo.GetValue(anonymousObject, null), pitem.FieldType)); }
                            catch { }
                        }
                    }
                );

                return typeobj;
            }
            catch (Exception ex)
            {
                throw ex;
            }
        }

        public static T BindVarsToPropertiesValues<T>(this object anonymousObject)
        {
            try
            {
                if (anonymousObject == null) return (T)anonymousObject;

                Type tSrcObject = anonymousObject.GetType();
                T typeobj = Activator.CreateInstance<T>();
                Type tDesObject = typeobj.GetType();


                FieldInfo[] pfSrcobjs = tSrcObject.GetFields();
                PropertyInfo[] pfDescobjs = tDesObject.GetProperties();

                Parallel.ForEach(pfDescobjs, pitem =>
                    {
                        var pinfo = pfSrcobjs.Where(
                                                p =>
                                                    p.Name.Equals(pitem.Name, StringComparison.OrdinalIgnoreCase) &&
                                                    p.IsPrivate == false
                                                ).FirstOrDefault();

                        if (pinfo != null)
                        {
                            try { pitem.SetValue(typeobj, Convert.ChangeType(pinfo.GetValue(anonymousObject), pitem.PropertyType), null); }
                            catch { }
                        }
                    }
                );

                return typeobj;
            }
            catch (Exception ex)
            {
                throw ex;
            }
        }

        public static T BindVariblesValues<T>(this object anonymousObject)
        {
            try
            {
                if (anonymousObject == null) return (T)anonymousObject;

                Type tSrcObject = anonymousObject.GetType();
                T typeobj = Activator.CreateInstance<T>();
                Type tDesObject = typeobj.GetType();


                FieldInfo[] pfSrcobjs = tSrcObject.GetFields();
                FieldInfo[] pfDescobjs = tDesObject.GetFields();

                Parallel.ForEach(pfDescobjs, pitem =>
                    {
                        var pinfo = pfSrcobjs.Where(
                                                p =>
                                                    p.Name.Equals(pitem.Name, StringComparison.OrdinalIgnoreCase) &&
                                                    p.IsPrivate == false &&
                                                    p.FieldType == pitem.FieldType
                                                ).FirstOrDefault();

                        if (pinfo != null)
                        {
                            try { pitem.SetValue(typeobj, Convert.ChangeType(pinfo.GetValue(anonymousObject), pinfo.FieldType)); }
                            catch { }
                        }
                    }
                );

                return typeobj;
            }
            catch (Exception ex)
            {
                throw ex;
            }
        }

        public static void SetVariblesValues(this object anonymousObject, IDictionary<string, string> Mapping)
        {
            try
            {
                if (anonymousObject == null) return;

                Type tSrcObject = anonymousObject.GetType();
                FieldInfo[] pfSrcobjs = tSrcObject.GetFields();

                Parallel.ForEach(pfSrcobjs, pitem => 
                    {
                        var pinfo = Mapping.Where(
                                                p =>
                                                    p.Key.Equals(pitem.Name, StringComparison.OrdinalIgnoreCase)
                                                    ).FirstOrDefault();

                        if (pinfo.isNull() == false)
                        {
                            try { pitem.SetValue(anonymousObject,  Convert.ChangeType(pinfo.Value, pitem.FieldType)); }
                            catch { }
                        }
                    }
                );
            }
            catch (Exception ex)
            {
                throw ex;
            }
        }

        

    }
}
